/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import toastManager from '../utils/toastManager';
import Logger from '../utils/Logger'
import { AccessibilityElement } from '@kit.AccessibilityKit';


export default class AccessibilityUtils {
  static p1 = null

  constructor() {
    AccessibilityUtils.p1 = new toastManager()
  }

  public static resultNumArray: any[] = [];

  static async getAttributeValue(accessibilityElement, key, trialCnt = 0) {
    if (accessibilityElement == null || trialCnt > 10) {
      console.error("_:_[getValue]!!!! AccessibilityUtils accessibilityElement is null")
      AccessibilityUtils.p1.Toast("[getValue]!!!! AccessibilityUtils accessibilityElement is null")
      return false
    }
    let value = undefined
    let isExit = true
    if (isExit) {
      try {
        value = await accessibilityElement.attributeValue(key)
      } catch (error) {
        console.error("_:_[getValue] AccessibilityUtils Failed to get attribute Value of " + key + " elem=" +
          accessibilityElement + ". Cause: " + JSON.stringify(error))
        return await this.getAttributeValue(accessibilityElement, key, trialCnt + 1)
      }
    }
    return value
  }

  static RectStr(rect, left = false) {
    return "[top:" + rect.top + "] [left:" + rect.left + "][width:" + rect.width + "] [height:" + rect.height + "]"
  }

  static async getElementWantedInfoPrint(element) {
    let wantedAttributes = //"triggerAction",
      ["componentId", "inspectorKey", "bundleName", "componentType", "inputType", "text", "hintText", "description",
        "textMoveUnit", "contents", "lastContent", "itemCount", "currentIndex", "startIndex",
        "endIndex", "resourceName", "textLengthLimit", "rect", "checkable", "checked", "focusable", "isVisible",
        "selected", "clickable", "longClickable", "isEnable", "isPassword", "scrollable", "editable",
        "pluralLineSupported", "parent", "children", "isFocused", "accessibilityFocused", "error", "isHint", "pageId",
        "valueMax", "valueMin", "valueNow", "windowId", "accessibilityText", "textType", "offset", "allAttribute"]
    if (element == null) {
      console.error("_:_AccessibilityUtils accessibilityElement is null")
      return null
    }
    let info4LLM = ""
    let infoPrint = ""
    let para = []
    let valuelist = []
    for (let name of wantedAttributes) {
      para.push(AccessibilityUtils.getAttributeValue(element, name))
    }
    valuelist = await Promise.all(para)
    let i = -1
    for (let name of wantedAttributes) {
      i++
      let value = ""
      if (name == "rect") {
        value = AccessibilityUtils.RectStr(valuelist[i], true)
      } else {
        value = valuelist[i]
      }
      if (name == "text" && value.replace("null", "").replace("0", "").replace(" ", "") != "") {
        info4LLM = value.replace("null", "") + " "
        infoPrint += info4LLM
      }
      if (name == "componentId") {
        infoPrint += name + ":" + value + " "
      } else if (name == "allAttribute") {
        infoPrint += name + ":" + JSON.stringify(value)
      } else if (value) {
        infoPrint += name + ":" + value + " "
      }
    }
    return [info4LLM, infoPrint]
  }

  static async getAllElementInfoPrint(currentPageElementArray, ElementLevelArray) {
    let fullInfo4LLM = ""
    let id
    let paraii = []
    let paraid = []
    let infos2 = []
    let ids = []
    for (let i = 0; i < currentPageElementArray.length; i++) {
      paraii.push(AccessibilityUtils.getElementWantedInfoPrint(currentPageElementArray[i]))
      paraid.push(AccessibilityUtils.getAttributeValue(currentPageElementArray[i], 'componentId'))
    }
    infos2 = await Promise.all(paraii)
    ids = await Promise.all(paraid)
    for (let i = 0; i < currentPageElementArray.length; i++) {
      console.log("__:__[node] " + "| ".repeat(ElementLevelArray[i]) + "| " + infos2[i][1])
      if (infos2[i][0] != "") {
        id = ids[i]
        fullInfo4LLM = fullInfo4LLM.concat("{<" + id + ">" + infos2[i][0] + "}")
      }
    }
    return fullInfo4LLM
  }

  static async getAllElementInfo(currentPageElementArray, getId = false) {
    let fullInfo4LLM = ""
    let paraInfo = []
    let paraId = []
    let Infos = []
    let Ids = []
    for (let i = 0; i < currentPageElementArray.length; i++) {
      paraInfo.push(AccessibilityUtils.getElementWantedInfo(currentPageElementArray[i]))
      if (getId) {
        paraId.push(AccessibilityUtils.getAttributeValue(currentPageElementArray[i], 'componentId'))
      }
    }
    switch (getId) {
      case true:
        [Infos, Ids] = await Promise.all([Promise.all(paraInfo), Promise.all(paraId)])
        let iid = -1
        for (let info of Infos) {
          iid++
          if (info != "") {
            fullInfo4LLM = fullInfo4LLM.concat("{<" + Ids[iid] + ">" + info + "}")
          }
        }
        break
      case false:
        Infos = await Promise.all(paraInfo)
        for (let info of Infos) {
          if (info != "") {
            fullInfo4LLM = fullInfo4LLM.concat("{<>" + info + "}")
          }
        }
        break
    }
    return fullInfo4LLM
  }

  static async getElementWantedInfo(element) {
    if (element == null) {
      console.error("__:__AccessibilityUtils accessibilityElement is null")
      return null
    }
    let info4LLM = ""
    let value = await AccessibilityUtils.getAttributeValue(element, "text")
    if (value.replace("null", "").replace("0", "").replace(" ", "") != "") {
      info4LLM = value.replace("null", "") + " "
    }
    return info4LLM
  }

  static clickAnsObj(Objs, key, value) {
    console.log("_:_[clickObj] key=" + key + " value=" + value)
    let foundObj = Objs.find((obj) => obj[key] == value)
    if (!foundObj) {
      console.log("_:_[clickObj] not found key=" + key + " value=" + value)
      return false
    } else {
      console.log("_:_[clickObj] found " + AccessibilityUtils.ObjInfo(foundObj))
    }
    let parentObj = foundObj
    while (!parentObj["clickable"]) {
      parentObj = parentObj["parent"]
    }
    if (!parentObj) {
      console.log("_:_[clickObj] parent found for " + AccessibilityUtils.ObjInfo(foundObj))
    } else {
      console.log("_:_[clickObj] parent not found for " + AccessibilityUtils.ObjInfo(foundObj) + " :" +
      AccessibilityUtils.ObjInfo(parentObj))
    }
    parentObj["origin"].performAction("click")
    console.log("_:_[clickObj] clicked:" + AccessibilityUtils.ObjInfo(parentObj))
    return true
  }

  static async createElementArray(accessibilityElement) {
    console.log("AccessibilityUtils createElementArray")
    let resultArray = []
    AccessibilityUtils.resultNumArray = []
    if (accessibilityElement == null) {
      console.error("AccessibilityUtils accessibilityElement is null.")
      return [resultArray, AccessibilityUtils.resultNumArray]
    }
    let stack = []
    let stacknum = []
    stack.push(accessibilityElement)
    stacknum.push(1)
    let tmpElement
    let tmplevel
    while (stack.length > 0) {
      tmpElement = stack.pop()
      tmplevel = stacknum.pop()
      resultArray.push(tmpElement)
      AccessibilityUtils.resultNumArray.push(tmplevel)
      let childElements = await AccessibilityUtils.getAttributeValue(tmpElement, "children")
      if (childElements) {
        for (let childElement of childElements) {
          if (childElement != null) {
            stacknum.push(tmplevel + 1)
            stack.push(childElement)
          }
        }
      } else {
        console.error("AccessibilityUtils this node does not have childElement")
      }
    }
    return [resultArray, AccessibilityUtils.resultNumArray]
  }

  static async findElementByKey(elementArray, targetElement) {
    console.log("_:_[findElementByKey] finding... target key [" + targetElement.KEY + "] value [" +
    targetElement.VALUE + "]  elementArray=" + elementArray.length)
    if (elementArray == null || elementArray.length == 0 || targetElement == null) {
      console.log("_:_[findElementByKey] returned!!! elementArray=" + elementArray.length + " targetElement=" +
        targetElement)
      return null
    }
    let parav = []
    let values = []
    let value = undefined
    for (let element of elementArray) {
      parav.push(AccessibilityUtils.getAttributeValue(element, targetElement.KEY))
    }
    let i = -1
    values = await Promise.all(parav)
    for (let value of values) {
      i++
      if (targetElement.KEY == "rect") {
        if (AccessibilityUtils.RectStr(value) == AccessibilityUtils.RectStr(targetElement.VALUE)) {
          return elementArray[i]
        }
      } else {
        if (value == targetElement.VALUE) {
          return elementArray[i]
        }
      }
    }
    console.error("_:_[findElementByKey] Failed to find target element. target key [" + targetElement.KEY +
      "] value [" +
    targetElement.VALUE + "  elementArray=" + elementArray)
    return null
  }

  static async findElementListByKey(elementArray, targetElement) {
    console.log("AccessibilityUtils findElementByKey")
    if (elementArray == null || elementArray.length == 0 || targetElement == null) {
      return null
    }
    let parav = []
    let values = []
    let resultList = []
    let value = undefined
    for (let element of elementArray) {
      parav.push(AccessibilityUtils.getAttributeValue(element, targetElement.KEY))
    }
    let i = -1
    values = await Promise.all(parav)
    for (let value of values) {
      i++
      if (targetElement.KEY == "rect") {
        if (AccessibilityUtils.RectStr(value) == AccessibilityUtils.RectStr(targetElement.VALUE)) {
          resultList.push(elementArray[i])
        }
      } else {
        if (value == targetElement.VALUE) {
          resultList.push(elementArray[i])
        }
      }
    }
    return resultList
  }

  static async traceBrotherAttribute(element, attribute) {
    let father = await AccessibilityUtils.getAttributeValue(element, "parent")
    let children = await AccessibilityUtils.getAttributeValue(element, "children")
    for (let child of children) {
      let clickable = await AccessibilityUtils.getAttributeValue(child, "clickable")
      if (clickable) {
        return child
      }
    }
    console.log("_:_ failed to trace brother Attribute")
    return element
  }

  static async traceAncestorAttribute(element, attribute) {
    let tmpElement = element
    let tmpattribute
    while (true) {
      tmpElement = await AccessibilityUtils.getAttributeValue(tmpElement, "parent")
      if (!tmpElement) {
        break
      } else {
        tmpattribute = await AccessibilityUtils.getAttributeValue(tmpElement, attribute)
        if (tmpattribute) {
          return tmpElement
        }
      }
    }
    console.log("traceParentAttribute fail:" + attribute)
    return tmpElement
  }

  static examineOverlap(objs) {
    let TAG = "_:_examineOverlap"
    let rtn = false
    objs.map((obj) => {
      if (obj["overlapText"] != "") {
        rtn = true
        return true
      } else if (obj["clickable"] == true) {
        rtn = true
        return true
      }
      return false
    })
    return rtn
  }

  static async getCurrentPageAllObjs(context, print = false, trialCnt = 0, rootElement = null) {
    let TAG = "_:_>>>>[updating]"
    // console.error(TAG, "entered ", "getCurrentPageAllObjs")
    if (trialCnt > 5) {
      AccessibilityUtils.p1.Toast("Error getting CurrentPageAllObjs")
      return false
    }
    let time1: Date = new Date();
    if (rootElement == null) {
      try {
        rootElement = await context.getWindowRootElement()
      } catch (error) {
        console.log(TAG,
          "[!update] ElementArrayOBJ Failed to getWindowRootElement. Cause: " + error + JSON.stringify(error))
        return await AccessibilityUtils.getCurrentPageAllObjs(context, print, trialCnt + 1)
      }
    }
    let [currentPageElementArray, ElementLevelArray] = await AccessibilityUtils.createElementArray(rootElement)
    currentPageElementArray = currentPageElementArray.reverse()
    let objs = await AccessibilityUtils.EAtoObject(currentPageElementArray)
    let time2: Date = new Date();
    let costTime = time2.getTime() - time1.getTime();
    if (!print) {
      return {
        elems: currentPageElementArray, DOMs: objs
      }
    } else {
      await AccessibilityUtils.getAllElementInfoPrint(currentPageElementArray, ElementLevelArray)
      return {
        elems: objs, DOMs: currentPageElementArray
      }
    }
  }

  static async getCurrentPageAllElement(context, print = false) {
    let rootElement = null
    try {
      rootElement = await context.getWindowRootElement()
    } catch (error) {
      console.error("AccessibilityExtAbility Failed to getWindowRootElement. Cause: " + JSON.stringify(error))
    }
    let [currentPageElementArray, ElementLevelArray] = await AccessibilityUtils.createElementArray(rootElement)
    currentPageElementArray = currentPageElementArray.reverse()
    console.log("_:_<<<<[updated ] ElementArray" + currentPageElementArray)
    if (!print) {
      return currentPageElementArray
    } else {
      AccessibilityUtils.getAllElementInfoPrint(currentPageElementArray, ElementLevelArray)
      return currentPageElementArray
    }
  }

  static async findScrollable(currentPageElementArray) {
    let targetElementscroll = {
      KEY: "scrollable",
      VALUE: true
    }
    return await AccessibilityUtils.findElementByKey(currentPageElementArray, targetElementscroll)
  }

  static async findClickRect(currentPageElementArray, click = false, rect = {
    left: 54,
    top: 96,
    width: 36,
    height: 36
  }, waitTime = 300) {
    console.log("_:_[tree] [findClickRect] entered")
    let targetElementRect = {
      KEY: "rect",
      VALUE: rect
    }
    let targetElementClickable = {
      KEY: "clickable",
      VALUE: true
    }
    let selectedElements = await AccessibilityUtils.findElementListByKey(currentPageElementArray, targetElementRect)
    if (selectedElements == null || selectedElements.length == 0 || (!selectedElements)) {
      console.log("_:_[tree] [sELEs] returning due to null sELEs " + selectedElements)
      return null
    }
    console.log("_:_[tree] [sELEs] " + selectedElements)
    let selectedElement = await AccessibilityUtils.findElementByKey(selectedElements, targetElementClickable)
    console.log("_:_[tree] [sELE ] " + selectedElement)
    if (click) {
      selectedElement.performAction("click")
      console.log("_:_[tree] ####[pref act] click [returned]")
    }
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(true)
      }, waitTime)
    })
  }

  static async findClickables(currentPageElementArray, getEntrySum = true) {
    let targetClickableElement = {
      KEY: "clickable",
      VALUE: true
    }
    let clickableEntries = []
    let clickableElements =
      await AccessibilityUtils.findElementListByKey(currentPageElementArray, targetClickableElement)
    let returnElement = await AccessibilityUtils.findClickRect(clickableElements)
    console.log("_:_[E.E.] findClickables returnElement=" + returnElement + " clickableElements.length=" +
    clickableElements.length)
    for (let clickable of clickableElements) {
      if (clickable != returnElement) {
        clickableEntries.push(clickable)
      }
    }
    if (!getEntrySum) {
      let entries_exit = {
        entries: clickableEntries,
        exit: returnElement
      }
      return clickableEntries
    } else {
      return clickableEntries.length
    }
  }

  static printOneNode(node) {
    console.log("_:_[printNode] entries.length [" + node.entries.length + "]")
    console.log("_:_[         ] entriesDesc    [" + node.entriesDesc + "]")
    console.log("_:_[         ] exit           [" + node.exit + "]")
    console.log("_:_[         ] parent         [" + node.parent + "]")
    console.log("_:_[         ] children.length[" + node.children.length + "]")
  }

  static async findClickableAns(currentPageElementArray, targetid) {
    let targetIdElement = {
      KEY: "componentId",
      VALUE: targetid
    }
    let tarnameElement = await AccessibilityUtils.findElementByKey(currentPageElementArray, targetIdElement)
    if (tarnameElement == null) {
      console.log("_:_!!!!no tarnameElement")
      return false
    }
    let toclickElement = await AccessibilityUtils.traceAncestorAttribute(tarnameElement, "clickable")
    if (toclickElement == null) {
      console.log("_:_!!!!no clickable Ancestor")
      return false
    }
    return toclickElement
  }

  static scrollDirs(forward) {
    if (forward) {
      return "scrollForward"
    } else {
      return "scrollBackward"
    }
  }

  static async sleep(time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(time)
      }, time)
    })
  }

  static async textfilter(element) {
    await AccessibilityUtils.getAttributeValue(element, "text")
  }

  static findClickablesObj(Objs) {
    return Objs.filter((obj) => {
      return obj["clickable"] == true
        && (!(obj["rect"].top == 96
          && obj["rect"].width == 36
          && obj["rect"].height == 36))
    })
  }

  static findReturnObj(Objs) {
    return Objs.find((obj) => {
      return (obj["clickable"] == true
        && obj["rect"].top == 96
        && obj["rect"].width == 36
        && obj["rect"].height == 36)
    })
  }

  static isOverlap(rectA, rectB) {
    rectA.right = rectA.left + rectA.width;
    rectA.bottom = rectA.top + rectA.height;
    rectB.right = rectB.left + rectB.width;
    rectB.bottom = rectB.top + rectB.height;
    return rectA.left < rectB.right &&
      rectA.right > rectB.left &&
      rectA.top < rectB.bottom &&
      rectA.bottom > rectB.top;
  }

  static minus_crop(val, subVal, minBoarder) {
    if (val - subVal > 0) {
      return val - subVal
    } else {
      return minBoarder
    }
  }

  static add_crop(val, addVal, maxBoarder) {
    if (val + addVal < maxBoarder) {
      return val + addVal
    } else {
      return maxBoarder
    }
  }

  static isPointInRect(rect, pointX, pointY, approximate = 0): boolean {
    rect.right = rect.left + rect.width;
    rect.bottom = rect.top + rect.height;
    rect.left -= approximate;
    rect.right += approximate;
    rect.top -= approximate;
    rect.bottom += approximate;
    rect.left = Math.max(0, Math.min(720, rect.left));
    rect.right = Math.max(0, Math.min(720, rect.right));
    rect.top = Math.max(0, Math.min(1280, rect.top));
    rect.bottom = Math.max(0, Math.min(1280, rect.bottom));
    return pointX >= rect.left && pointX <= rect.right &&
      pointY >= rect.top && pointY <= rect.bottom;
  }

  static getOverlapText(elements) {
    let TAG = "_:_overLap"
    for (let clickable of elements) {
      if (clickable["clickable"] == true) {
        for (let nclickable of elements) {
          if (nclickable["clickable"] == false) {
            if (AccessibilityUtils.isOverlap(clickable["rect"], nclickable["rect"])
            ) {
              if (nclickable["text"] != undefined && nclickable["text"] != "" && nclickable["text"] != "0") {
                clickable["overlapText"] += nclickable["text"] + ","
              }
            }
          }
        }
        if (clickable["overlapText"] != "") {
          clickable["overlapText"] = clickable["overlapText"].slice(0, -1)
        }
      }
    }
    return elements
  }

  static async EAtoObject(elements,
    wantedAttributes = ['children', 'componentId', 'text', 'checkable', 'clickable', 'scrollable', 'rect', 'pageId',
      'windowId', 'accessibilityText', 'hintText', 'componentType']) {
    let TAG = "_:_>>>>[EA]"
    let resultss = await Promise.all(
      elements.map((element) => {
        let para = []
        for (let key of wantedAttributes) {
          para.push(element.attributeValue(key))
        }
        return Promise.all(para)
      })
    );
    let elementsObj = resultss.map((results: any[], index) => {
      let attributes = {};
      results.map((result, indexi) => {
        attributes[wantedAttributes[indexi]] = result
      })
      attributes["origin"] = elements[index]
      attributes["overlapText"] = ""
      if (typeof attributes["accessibilityText"] === undefined) {
        attributes["accessibilityText"] = ""
      }
      if (typeof attributes["hintText"] === undefined) {
        attributes["hintText"] = ""
      }
      return attributes
    })
    let parentObjs = await Promise.all(elementsObj.map((elementObj, index) => {
      try {
        return elementObj[index].attributeValue("parent");
      } catch (e) {
        return new Promise((resolve) => {
          resolve(null)
        })
      }
    }))
    elementsObj.map((elementObj, index) => {
      elementObj["parent"] = parentObjs[index]
    })
    try {
      elementsObj = await AccessibilityUtils.buildTree(elementsObj);
    } catch (e) {
      console.log(TAG, "error in building tree", e)
    }
    try {
      elementsObj = await AccessibilityUtils.getOverlapText(elementsObj)
    } catch (e) {
      console.log(TAG, "error in overlap", e)
    }
    return elementsObj
  }

  static async findObjFromEle(Objs, element) {
    let TAG = "_:_[findObjFromEle]"
    try {
      let id = await element.attributeValue("componentId")
      let foundObj = Objs.find(obj => obj["componentId"] == id)
      return foundObj
    } catch (e) {
      console.log(TAG, "fail ", e)
    }
  }

  static async buildTree(Objs) {
    try {
      return Promise.all(
        Objs.map(async (obj) => {
          let TAG = "_:_buildTree Objs->Obj"
          try {
            if (obj["children"]) {
              obj["children"] = await
              Promise.all(
                obj["children"].map(async (child) => {
                  return AccessibilityUtils.findObjFromEle(Objs, child)
                })
              )
            }
          } catch (e) {
            console.log(TAG, "find children", e)
          }
          try {
            if (obj["parent"] != null && obj["parent"] != undefined) {
              obj["parent"] = await AccessibilityUtils.findObjFromEle(Objs, obj["parent"])
              if (obj["parent"] == null || obj["parent"] == undefined) {
                obj["parent"] = {
                  "componentId": "not defined"
                }
              }
            } else {
              obj["parent"] = {
                "componentId": "not defined"
              }
            }
          } catch (e) {
            console.log(TAG, "find parent", e)
          }
          return obj
        })
      )
    } catch (error) {
      console.error("_:_[bTree] buildTree error: ", error);
      return Objs // 重新抛出错误，以便调用者可以处理
    }
  }

  static hasDistillAttribute(elementObj) {
    let TAG = "_:_[hasDistill]"
    if (
      elementObj["clickable"] == true
        || elementObj["scrollable"] == true
        || elementObj["text"].trim() != ""
        || elementObj["text"].trim() != "0"
        || elementObj["accessibillityText"].trim() != ""
        || elementObj["overlapText"].trim() != ""
    ) {
      return true
    } else {
      return false
    }
  }

  static distill(elementObj, level = 0) {
    console.log("_:_[distill]" + "| ".repeat(level) + elementObj)
    if (AccessibilityUtils.hasDistillAttribute(elementObj)) {
      console.log("_:_[distill]" + "| ".repeat(level) + " has attribute " + AccessibilityUtils.ObjInfo(elementObj))
      elementObj["children"] = elementObj["children"].filter(child => {
        return AccessibilityUtils.distill(child, level + 1) !== null;
      });
      return elementObj;
    }
    console.log("_:_[distill]" + "| ".repeat(level) + "donthas attribute " + AccessibilityUtils.ObjInfo(elementObj))
    const childrenWithoutDesiredAttribute = elementObj["children"].map((child, index, children) => {
      console.log("_:_[distill]" + "| ".repeat(level) + "donthas attribute, checking the children .length:[" +
      children.length + "]  original:" + AccessibilityUtils.ObjInfo(elementObj) + "  child:" +
      AccessibilityUtils.ObjInfo(child))
      return AccessibilityUtils.distill(child, level + 1);
    }).filter(child => child !== null);
    if (childrenWithoutDesiredAttribute.length === 0) {
      console.log("_:_[distill]" + "| ".repeat(level) + "result childrenlength=0 " +
      AccessibilityUtils.ObjInfo(elementObj))
      return null;
    }
    let parent = elementObj
    parent["children"] = childrenWithoutDesiredAttribute
    console.log("_:_[distill]" + "| ".repeat(level) + " returning eleObj:[" + AccessibilityUtils.ObjInfo(elementObj) +
      " childrenlength=[" + childrenWithoutDesiredAttribute + "] parent:[" + AccessibilityUtils.ObjInfo(parent) + "]")
    return parent;
  }

  static getJSONinfo(elementsObj, plainStore = false) {
    let rootObj
    if (elementsObj.length == 1) {
      rootObj = elementsObj[0]
    } else {
      rootObj = elementsObj[elementsObj.length-1]
    }
    if (!plainStore) {
      return AccessibilityUtils.JSONreculsiveTrim(rootObj).replace(/\\"/g, "")
    } else {
      return AccessibilityUtils.JSONreculsiveTrim(rootObj, true)
        .replace(/[\\"{}\[\]]/g, '').replace(/,+/g, ",")
    }
  }

  static JSONreculsiveTrim(elementObj, plain = false, lastSingle = false) {
    let TAG = "_:_[JSONr]"
    if (elementObj["children"].length == 0) {
      if (AccessibilityUtils.examineOverlap([elementObj])) {
        console.log(TAG, "examine tail", AccessibilityUtils.ObjInfo(elementObj, plain))
      }
      return AccessibilityUtils.ObjInfo(elementObj, plain)
    } else {
      let childrenList = elementObj["children"].map((child) => {
        if (AccessibilityUtils.examineOverlap([child])) {
          return AccessibilityUtils.ObjInfo(child, plain).trim()
        }
        return AccessibilityUtils.JSONreculsiveTrim(child, plain)
      }).filter(child => child != "")
      if (childrenList.length == 1) {
        return childrenList[0]
      } else {
        return AccessibilityUtils.listStringify(childrenList)
      }
    }
  }

  static listStringify(list) {
    if (list.length == 0) {
      return ""
    } else {
      let rtnstr = "["
      for (let item of list) {
        if (item != "") {
          rtnstr += item + ","
        }
      }
      return rtnstr.slice(0, -1) + "]"
    }
  }

  static ObjsInfo(Objs, plain = false, text = "") {
    let cnt = 0
    console.log("_:_-------------------ObjsInfo start-------------------", Objs.length)
    console.log("_:_", Objs)
    for (let obj of Objs) {
      console.log("_:_[ObjsInfo]", cnt, text, AccessibilityUtils.ObjInfo(obj, plain))
      cnt++
    }
    console.log("_:_-------------------ObjsInfo end  -------------------")
    return cnt
  }

  static pageNodeInfo(pagenode) {
    let newNode = pagenode
    newNode.parentL = {}
    newNode.childrenL = []
    return JSON.stringify(newNode)
  }

  static ObjInfo(Obj, plain = false, learnInfo = false) {
    let TAG = "_:_objInfo"
    if (Obj == null || Obj == undefined) {
      console.log(TAG, "object input is null")
    }
    if (AccessibilityUtils.hasDistillAttribute(Obj)) {
      let condT =
        (Obj["text"] != null && Obj["text"] != undefined && Obj["text"].trim() != "" && Obj["text"].trim() != "0")
      let condO = Obj["overlapText"] != null && Obj["overlapText"] != undefined && Obj["overlapText"].trim() != ""
      let condA = Obj["accessibilityText"] != null && Obj["accessibilityText"] != undefined &&
        Obj["accessibilityText"].trim() != ""
      let condH = Obj["hintText"] != null && Obj["hintText"] != undefined &&
        Obj["hintText"].trim() != ""
      if (plain == false) {
        let rtnstr = ""
        if (!learnInfo) {
          rtnstr += "{id:" + Obj["componentId"] + ","
        }
        if (condT || condO || condA || condH) {
          rtnstr += "text:"
        }
        if (condH) {
          rtnstr += Obj["hintText"] + ","
        }
        if (condT) {
          rtnstr += Obj["text"] + ","
        }
        if (condA) {
          rtnstr += Obj["accessibilityText"] + ","
        }
        if (condO) {
          rtnstr += "(" + Obj["overlapText"] + "),"
        }
        if (Obj["componentType"] == "TextInput" || Obj["componentType"] == "TextArea") {
          rtnstr += "type:TextInput,"
        }
        if (Obj["clickable"]) {
          rtnstr += "clickable,"
        }
        if (Obj["scrollable"]) {
          rtnstr += "scrollable,"
        }
        rtnstr = rtnstr.slice(0, -1)
        if (!learnInfo) {
          rtnstr += "}"
        }
        return rtnstr
      } else {
        let rtnstr = "{"
        if (condT) {
          rtnstr += Obj["text"] + ","
        }
        if (condA) {
          rtnstr += Obj["accessibilityText"] + ","
        }
        if (condO) {
          rtnstr += Obj["overlapText"] + ","
        }
        if (condH) {
          rtnstr += Obj["hintText"] + ","
        }
        rtnstr += "}"
        return rtnstr
      }
    } else {
      console.log(TAG, "returning empty")
      return ""
    }
  }
}
